using System;
using System.Collections.Generic;
using System.Net;
using RestSharp;
using RestSharp.Deserializers;

namespace FenopyApi
{
    public static class FenopyClient
    {
        private static RestClient MakeClient()
        {
            RestClient client = new RestClient("http://fenopy.eu/");
            client.AddHandler("text/html", new JsonDeserializer());

            return client;
        }

        /// <summary>
        /// Perform a basic search
        /// </summary>
        /// <param name="keyword">Keyword(s) to search for</param>
        /// <returns></returns>
        public static List<SearchResult> Search(string keyword)
        {
            // Pass an invalid limit value to have it ignored
            return Search(keyword, 0);
        }

        /// <summary>
        /// Perform a basic search
        /// </summary>
        /// <param name="keyword">Keyword(s) to search for</param>
        /// <param name="limit">Limit between 1 and 100</param>
        /// <returns></returns>
        public static List<SearchResult> Search(string keyword, int limit)
        {
            // Pass an invalid Category to have it ignored
            return Search(keyword, limit, (Category)int.MaxValue);
        }

        /// <summary>
        /// Perform a basic search
        /// </summary>
        /// <param name="keyword">Keyword(s) to search for</param>
        /// <param name="limit">Limit between 1 and 100</param>
        /// <param name="category">Category to stay within</param>
        /// <returns></returns>
        public static List<SearchResult> Search(string keyword, int limit, Category category)
        {
            // Pass an invalid Sorting value to have it ignored
            return Search(keyword, limit, category, (Sorting)int.MaxValue);
        }

        /// <summary>
        /// Perform a basic search
        /// </summary>
        /// <param name="keyword">Keyword(s) to search for</param>
        /// <param name="limit">Limit between 1 and 100</param>
        /// <param name="category">Category to stay within</param>
        /// <param name="sorting">Sorting to apply</param>
        /// <returns></returns>
        public static List<SearchResult> Search(string keyword, int limit, Category category, Sorting sorting)
        {
            RestClient client = MakeClient();

            RestRequest req = new RestRequest("module/search/api.php", Method.GET);
            req.AddParameter("format", "json");
            req.AddParameter("keyword", keyword);

            if (1 <= limit && limit <= 100)
                req.AddParameter("limit", limit);

            if (Enum.IsDefined(typeof(Sorting), sorting))
                req.AddParameter("sort", sorting.ToString().ToLower());

            if (Enum.IsDefined(typeof(Category), category))
                req.AddParameter("category", (int)category);

            IRestResponse<List<SearchResult>> resp = client.Execute<List<SearchResult>>(req);

            if (resp.ErrorException != null)
            {
                if (resp.Content == "error: no match found.")
                    // No results - return empty list, don't crash
                    return new List<SearchResult>();

                throw resp.ErrorException;
            }
            else if (resp.ErrorMessage != null)
            {
                throw new Exception(resp.ErrorMessage);
            }

            return resp.Data;
        }

        /// <summary>
        /// Determine if the given InfoHash represents a fake torrent
        /// </summary>
        /// <param name="hash">20 byte infohash</param>
        /// <returns>Fake status</returns>
        public static FakeResponse IsFake(byte[] hash)
        {
            if (hash == null)
                throw new ArgumentNullException("hash");

            if (hash.Length != 20)
                throw new ArgumentException("hash length must be 20");

            return IsFake(BitConverter.ToString(hash).Replace("-", ""));
        }

        /// <summary>
        /// Determine if the given InfoHash represents a fake torrent
        /// </summary>
        /// <param name="hash">20 byte infohash as a 40-char hexadecimal string</param>
        /// <returns>Fake status</returns>
        public static FakeResponse IsFake(string hash)
        {
            if (hash == null)
                throw new ArgumentNullException("hash");

            if (hash.Length != 40)
                throw new ArgumentException("hash length must be 40 chars");

            RestClient client = MakeClient();

            RestRequest req = new RestRequest("fakefinder/api.php", Method.GET);
            req.AddParameter("hash", hash);

            IRestResponse resp = client.Execute(req);

            if (resp.ErrorException != null)
                throw resp.ErrorException;

            FakeResponse res = ParseResponse(resp.Content);

            return res;
        }

        private static FakeResponse ParseResponse(string response)
        {
            switch (response)
            {
                case "fake":
                    return FakeResponse.Fake;
                case "verified":
                    return FakeResponse.Verified;
                case "neutral":
                    return FakeResponse.Neutral;
            }

            throw new ArgumentException("Unknown value: response");
        }
    }
}